<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>B站知识区互动率排行榜</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
    <style>
        :root {
            --primary-color: #FB7299;
            --secondary-color: #23ADE5;
            --accent-color: #F8B195;
            --text-color: #333;
            --light-text: #666;
            --lighter-text: #999;
            --bg-color: #f5f7fa;
            --card-bg: #fff;
            --border-color: #e5e9ef;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            font-family: 'Noto Sans SC', 'Microsoft YaHei', sans-serif;
            background-color: var(--bg-color);
            color: var(--text-color);
            line-height: 1.6;
            padding: 20px;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
        }

        .header {
            text-align: center;
            margin-bottom: 30px;
            padding: 20px;
            background-color: var(--card-bg);
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
        }

        .header h1 {
            color: var(--primary-color);
            font-size: 2.2rem;
            margin-bottom: 10px;
            font-weight: 700;
        }

        .header p {
            color: var(--light-text);
            font-size: 1rem;
        }

        .tabs {
            display: flex;
            justify-content: center;
            margin-bottom: 25px;
            background-color: var(--card-bg);
            padding: 10px;
            border-radius: 50px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
        }

        .tab {
            padding: 12px 25px;
            margin: 0 5px;
            cursor: pointer;
            font-weight: 600;
            color: var(--light-text);
            border-radius: 50px;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
        }

        .tab.active {
            color: white;
            background-color: var(--primary-color);
            box-shadow: 0 4px 15px rgba(251, 114, 153, 0.3);
        }

        .tab:hover:not(.active) {
            color: var(--primary-color);
            background-color: rgba(251, 114, 153, 0.1);
        }

        .chart-container {
            background-color: var(--card-bg);
            border-radius: 12px;
            padding: 20px;
            margin-bottom: 30px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
            position: relative;
            min-height: 600px;
        }

        #chart {
            width: 100%;
            height: 550px;
        }

        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
            color: var(--light-text);
            font-size: 1.1rem;
        }

        .loading-spinner {
            border: 4px solid rgba(251, 114, 153, 0.2);
            border-radius: 50%;
            border-top: 4px solid var(--primary-color);
            width: 40px;
            height: 40px;
            animation: spin 1s linear infinite;
            margin: 0 auto 15px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        .data-info {
            text-align: center;
            margin-top: 15px;
            color: var(--light-text);
            font-size: 0.9rem;
        }

        .no-data-message {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
            color: var(--light-text);
            font-size: 1.2rem;
            width: 100%;
            padding: 0 20px;
        }

        @media (max-width: 768px) {
            .header h1 {
                font-size: 1.8rem;
            }

            .tabs {
                flex-wrap: wrap;
                border-radius: 12px;
            }

            .tab {
                padding: 10px 15px;
                margin: 5px;
                font-size: 0.9rem;
            }

            #chart {
                height: 500px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>B站知识区互动率排行榜</h1>
            <p>展示知识区视频的互动数据排行（鼠标悬停查看详情）</p>
        </div>

        <div class="tabs">
            <div class="tab active" data-type="likeRate">点赞率榜</div>
            <div class="tab" data-type="coinRate">投币率榜</div>
            <div class="tab" data-type="shareRate">转发率榜</div>
        </div>

        <div class="chart-container">
            <div id="chart">
                <div class="loading">
                    <div class="loading-spinner"></div>
                    <p>正在加载数据，请稍候...</p>
                </div>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 初始化图表
            const chartDom = document.getElementById('chart');
            const myChart = echarts.init(chartDom);
            const loadingElement = chartDom.querySelector('.loading');
            const tabs = document.querySelectorAll('.tab');

            // 图表配置
            const CHART_CONFIG = {
                likeRate: {
                    name: '点赞率排行',
                    unit: '%',
                    formatter: '{c}%',
                    colorRange: ['#FFD166', '#FF9A76', '#FF6B6B']
                },
                coinRate: {
                    name: '投币率排行',
                    unit: '%',
                    formatter: '{c}%',
                    colorRange: ['#A0E7E5', '#58D9F9', '#23ADE5']
                },
                shareRate: {
                    name: '转发率排行',
                    unit: '%',
                    formatter: '{c}%',
                    colorRange: ['#B5EAD7', '#8CD790', '#4CAF50']
                }
            };

            let processedData = [];
            let currentChartType = 'likeRate';

            // 初始化应用
            init();

            function init() {
                setupEventListeners();
                loadData();
            }

            function setupEventListeners() {
                // 标签切换事件
                tabs.forEach(tab => {
                    tab.addEventListener('click', function() {
                        tabs.forEach(t => t.classList.remove('active'));
                        this.classList.add('active');
                        currentChartType = this.dataset.type;
                        renderChart();
                    });
                });

                // 窗口大小改变时重新调整图表大小
                window.addEventListener('resize', function() {
                    myChart.resize();
                });
            }

            function loadData() {
                fetch('../static/knowledge.json')
                    .then(response => {
                        if (!response.ok) {
                            throw new Error('网络响应不正常');
                        }
                        return response.json();
                    })
                    .then(data => {
                        processData(data);
                        loadingElement.style.display = 'none';

                        if (processedData.length === 0) {
                            showNoDataMessage(); // 如果数据为空，显示提示信息
                        } else {
                            renderChart(); // 如果有数据，渲染图表
                        }
                    })
                    .catch(error => {
                        console.error('加载数据失败:', error);
                        showNoDataMessage();
                    });
            }

            function showNoDataMessage() {
                loadingElement.style.display = 'none';
                const chartContainer = document.querySelector('.chart-container');

                // 检查是否已经显示过提示信息
                if (!chartContainer.querySelector('.no-data-message')) {
                    const messageElement = document.createElement('div');
                    messageElement.className = 'no-data-message';
                    messageElement.innerHTML = '<p>目前缺少相关数据，请先进行爬取</p>';
                    chartContainer.appendChild(messageElement);
                }
            }

            function processData(rawData) {
                if (!rawData || rawData.length === 0) {
                    processedData = [];
                    return;
                }

                // 数据去重
                const uniqueMap = new Map();
                rawData.forEach(item => {
                    if (!uniqueMap.has(item.bvid)) {
                        uniqueMap.set(item.bvid, item);
                    }
                });

                // 计算各项比率
                processedData = Array.from(uniqueMap.values()).map(item => {
                    const views = Math.max(1, parseInt(item.views));

                    return {
                        name: formatTitle(item.title).short,
                        fullTitle: formatTitle(item.title).full,
                        bvid: item.bvid,
                        likeRate: (parseInt(item.likes) / views * 100).toFixed(2),
                        coinRate: (parseInt(item.coins) / views * 100).toFixed(2),
                        shareRate: (parseInt(item.shares) / views * 100).toFixed(2),
                        raw: {
                            views: item.views,
                            likes: item.likes,
                            coins: item.coins,
                            shares: item.shares,
                            stars: item.stars,
                            upName: item.up_name,
                            bulletComments: item.bullet_comments
                        }
                    };
                });
            }

            function formatTitle(title) {
                // 简化标题
                let cleanedTitle = title.replace(/【.*?】|\[.*?\]|《.*?》|（.*?）/g, '').trim();

                if (cleanedTitle.length > 15) {
                    return {
                        short: cleanedTitle.substring(0, 12) + '...',
                        full: title
                    };
                }

                return {
                    short: cleanedTitle,
                    full: title
                };
            }

            function renderChart() {
                // 如果数据为空，显示提示信息
                if (!processedData || processedData.length === 0) {
                    showNoDataMessage();
                    return;
                }

                // 移除可能存在的提示信息
                const existingMessage = document.querySelector('.no-data-message');
                if (existingMessage) {
                    existingMessage.remove();
                }

                const config = CHART_CONFIG[currentChartType];
                const sortedData = sortData(processedData, currentChartType).slice(0, 30);

                const option = {
                    title: {
                        text: config.name,
                        left: 'center',
                        textStyle: {
                            fontSize: 18,
                            fontWeight: 'bold',
                            color: '#333'
                        },
                        subtext: 'TOP 30 排行榜',
                        subtextStyle: {
                            color: '#666',
                            fontSize: 14
                        }
                    },
                    tooltip: {
                        trigger: 'item',
                        backgroundColor: 'rgba(255, 255, 255, 0.98)',
                        borderColor: '#e5e9ef',
                        borderWidth: 1,
                        padding: [12, 16],
                        textStyle: {
                            color: '#333',
                            fontSize: 14
                        },
                        formatter: function(params) {
                            const dataItem = sortedData[params.dataIndex];
                            const value = parseFloat(dataItem[currentChartType]).toFixed(2);
                            const color = getColorForValue(value, config.colorRange);

                            return '<div style="margin-bottom:8px;font-size:16px;font-weight:bold;color:' + color + '">' +
                                   dataItem.fullTitle + '</div>' +
                                   '<div style="margin-bottom:5px"><span style="display:inline-block;width:60px">UP主:</span>' +
                                   (dataItem.raw.upName || '未知') + '</div>' +
                                   '<div style="margin-bottom:5px"><span style="display:inline-block;width:60px">BV号:</span>' +
                                   (dataItem.bvid || '未知') + '</div>' +
                                   '<div style="border-top:1px dashed #e5e9ef;padding-top:8px;margin-top:8px">' +
                                   '<span style="color:' + color + ';font-weight:500">' +
                                   config.name.split('排行')[0] + ': ' + value + '%</span><br>' +
                                   '播放: ' + dataItem.raw.views.toLocaleString() + ' | ' +
                                   '点赞: ' + dataItem.raw.likes.toLocaleString() + ' | ' +
                                   '投币: ' + dataItem.raw.coins.toLocaleString() + '<br>' +
                                   '收藏: ' + dataItem.raw.stars.toLocaleString() + ' | ' +
                                   '转发: ' + dataItem.raw.shares.toLocaleString() + ' | ' +
                                   '弹幕: ' + dataItem.raw.bulletComments.toLocaleString() + '</div>';
                        }
                    },
                    grid: {
                        top: '20%',
                        right: '5%',
                        bottom: '15%',
                        left: '5%',
                        containLabel: true
                    },
                    xAxis: {
                        type: 'category',
                        data: sortedData.map(function(d) { return d.name; }),
                        axisLabel: {
                            rotate: 45,
                            interval: 0,
                            fontSize: 12,
                            color: '#666',
                            margin: 15,
                            overflow: 'truncate',
                            width: 80,
                            align: 'center'
                        },
                        axisLine: {
                            lineStyle: {
                                color: '#e5e9ef'
                            }
                        },
                        axisTick: {
                            alignWithLabel: true,
                            lineStyle: {
                                color: '#e5e9ef'
                            }
                        }
                    },
                    yAxis: {
                        type: 'value',
                        name: config.name.split('排行')[0] + ' (%)',
                        nameTextStyle: {
                            color: '#666',
                            padding: [0, 0, 0, 40]
                        },
                        axisLabel: {
                            formatter: '{value}%',
                            color: '#666'
                        },
                        splitLine: {
                            lineStyle: {
                                type: 'dashed',
                                color: '#e5e9ef'
                            }
                        },
                        axisLine: {
                            show: true,
                            lineStyle: {
                                color: '#e5e9ef'
                            }
                        }
                    },
                    series: [{
                        name: config.name.split('排行')[0],
                        type: 'bar',
                        data: sortedData.map(function(d) { return parseFloat(d[currentChartType]); }),
                        barWidth: '60%',
                        itemStyle: {
                            color: function(params) {
                                return getColorForValue(params.value, config.colorRange);
                            },
                            borderRadius: [6, 6, 0, 0],
                            shadowBlur: 4,
                            shadowColor: 'rgba(0, 0, 0, 0.1)'
                        },
                        label: {
                            show: true,
                            position: 'top',
                            formatter: config.formatter,
                            color: '#333',
                            fontSize: 12
                        },
                        emphasis: {
                            itemStyle: {
                                shadowBlur: 10,
                                shadowColor: 'rgba(0, 0, 0, 0.2)'
                            }
                        }
                    }],
                    dataZoom: [{
                        type: 'slider',
                        show: true,
                        xAxisIndex: 0,
                        start: 0,
                        end: 100,
                        bottom: '5%',
                        height: 16,
                        handleSize: '80%',
                        handleStyle: {
                            color: '#666'
                        },
                        textStyle: {
                            color: '#666'
                        }
                    }],
                    animationDuration: 1000,
                    animationEasing: 'cubicOut'
                };

                myChart.setOption(option, true);
            }

            function sortData(data, type) {
                return data.slice().sort(function(a, b) {
                    return parseFloat(b[type]) - parseFloat(a[type]);
                });
            }

            function getColorForValue(value, colorRange) {
                value = parseFloat(value);
                if (value > 15) return colorRange[2];
                if (value > 8) return colorRange[1];
                return colorRange[0];
            }
        });
    </script>
</body>
</html>